/*******************************************************************************
 * Copyright (c) 2007 by Ivo Woltring / Jamie Craane
 * All rights reserved.
 *******************************************************************************/
package org.acegicecurity.util;

import org.acegisecurity.Authentication;
import org.acegisecurity.ConfigAttributeDefinition;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.SecurityConfig;
import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
import org.acegisecurity.util.ExpressionEvaluator;
import org.acegisecurity.util.IllegalExpressionException;
import org.acegisecurity.util.expressioncompiler.LogicalExpressionCompiler;
import org.acegisecurity.util.expressioninterpreter.ExpressionInterpreter;

/**
 * Because we wrote two implementations of a logical expression parser/compiler we wrote this benchmark
 * to see which one performs better.
 * 
 * The {@link LogicalExpressionCompiler} has two possible benchmark tests:
 * 1 - compile once and match many
 * 2 - compile every time and match
 * the {@link ExpressionInterpreter} has one benchmark test:
 * 1 - like 2 of {@link LogicalExpressionCompiler}
 * 
 * NOTE: Don't forget to set the logging level to ERROR and NOT to DEBUG.
 *  
 * @author Ivo Woltring
 * @author Jamie Craane
 */
public class BenchMarkApp {
	private static final int MIDDLE_COUNT = 10;
	private static final int CALL_COUNT = 10000;
	private static final String EXPR = "(ROLE_A AND ROLE_B) AND NOT (ROLE_C OR ROLE_D) OR (ROLE_A AND ROLE_B AND NOT (ROLE_5 OR ROLE_6)) OR NOT NOT (ROLE_A AND (ROLE_B OR ROLE_C AND NOT (ROLE_4 OR ROLE_5))) AND NOT (ROLE_A AND ROLE_B) AND NOT (ROLE_C OR ROLE_D) OR (ROLE_A AND ROLE_B AND NOT (ROLE_5 OR ROLE_6)) OR NOT NOT (ROLE_A AND (ROLE_B OR ROLE_C AND NOT (ROLE_4 OR ROLE_5)))";

	
	private void benchmarkCompileOnce() {
		System.out.println("Testing: "+ MIDDLE_COUNT +" times "+ CALL_COUNT+" tests.");
		//Creating a test situation
        ConfigAttributeDefinition configAttributeDefinition = new ConfigAttributeDefinition();
        configAttributeDefinition.addConfigAttribute(new SecurityConfig(EXPR));
        GrantedAuthority[] authorities = new GrantedAuthorityImpl[] {
        		new GrantedAuthorityImpl("ROLE_A"),
        		new GrantedAuthorityImpl("ROLE_B"),
        		new GrantedAuthorityImpl("ROLE_C"),
        		new GrantedAuthorityImpl("ROLE_D"),
        		new GrantedAuthorityImpl("ROLE_E"), 
        		new GrantedAuthorityImpl("ROLE_F"), 
        };
        Authentication authentication = new AnonymousAuthenticationToken(" ", new Object(), authorities);
        
        //A bit of cleaning up before benchmarking if the VM wants to do it
		System.gc();
		
		long[] executionTimes = new long[MIDDLE_COUNT];
		/*
		 * Compile once benchmark
		 */
		LogicalExpressionCompiler.CompiledExpression compiledExpression = LogicalExpressionCompiler.parse(EXPR);
		for (int j = 0; j < MIDDLE_COUNT; j++) {
			long start = System.currentTimeMillis();
			for (int i = 0; i < CALL_COUNT; i++) {
				 compiledExpression.matches(authentication);
			}
			long end = System.currentTimeMillis();
			executionTimes[j] = end - start;
		}
		double result = 0d;
		for (int i = 0; i < executionTimes.length; i++) {
			result += executionTimes[i];
		}
		System.out.println("Execution time: " + result / executionTimes.length / CALL_COUNT + " millisecs average per transaction.");
		System.out.println("Execution time: " + result / executionTimes.length  + " millisecs average for "+ CALL_COUNT+" transactions.");
		
	}
	
	private void benchmarkEvalEveryTime(ExpressionEvaluator evaluator) throws IllegalExpressionException {
		//String expr = "ROLE_A and ROLE_B and (ROLE_C or ROLE_D)";
		System.out.println("Testing: "+ MIDDLE_COUNT +" times "+ CALL_COUNT+" tests.");
		//Creating a test situation
        ConfigAttributeDefinition configAttributeDefinition = new ConfigAttributeDefinition();
        configAttributeDefinition.addConfigAttribute(new SecurityConfig(EXPR));
        GrantedAuthority[] authorities = new GrantedAuthorityImpl[] {
        		new GrantedAuthorityImpl("ROLE_A"),
        		new GrantedAuthorityImpl("ROLE_B"),
        		new GrantedAuthorityImpl("ROLE_C"),
        		new GrantedAuthorityImpl("ROLE_D"),
        		new GrantedAuthorityImpl("ROLE_E"), 
        		new GrantedAuthorityImpl("ROLE_F"), 
        };
        Authentication authentication = new AnonymousAuthenticationToken(" ", new Object(), authorities);
        //A bit of cleaning up before benchmarking if the VM wants to do it
		System.gc();
		
		long[] executionTimes = new long[MIDDLE_COUNT];
		/*
		 * Compile every time benchmark 
		 */
		for (int j = 0; j < MIDDLE_COUNT; j++) {
			long start = System.currentTimeMillis();
			for (int i = 0; i < CALL_COUNT; i++) {
				evaluator.match(EXPR, authentication);
			}
			long end = System.currentTimeMillis();
			executionTimes[j] = end - start;
		}
		double result = 0d;
		for (int i = 0; i < executionTimes.length; i++) {
			result += executionTimes[i];
		}
		System.out.println("Execution time: " + result / executionTimes.length / CALL_COUNT + " millisecs average per transaction.");
		System.out.println("Execution time: " + result / executionTimes.length  + " millisecs average for "+ CALL_COUNT+" transactions.");
	} 

	/**
	 * @param args
	 * @throws IllegalExpressionException 
	 */
	public static void main(String[] args) throws IllegalExpressionException {
		BenchMarkApp app = new BenchMarkApp();

		
		System.out.println("===================================================================");
		System.out.println("The following (absurd) expression is evaluated:");
		System.out.println(EXPR);
		System.out.println("With the following roles: ROLE_A,ROLE_B,ROLE_C,ROLE_D,ROLE_E,ROLE_F");
		System.out.println("===================================================================");
		System.out.println("Benchmarking: LogicalExpressionCompiler when compiled Once...");
		app.benchmarkCompileOnce();
		System.out.println("===================================================================");
		System.out.println("Benchmarking: LogicalExpressionCompiler when compiled every time...");
		app.benchmarkEvalEveryTime(new LogicalExpressionCompiler());
		System.out.println("===================================================================");
		System.out.println("Benchmarking: ExpressionInterpreter (Parses expression every time)...");
		app.benchmarkEvalEveryTime(new ExpressionInterpreter());
		System.out.println("===================================================================");
		System.out.println("Finished.");		
	}
}
